﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;

namespace MySQL.Db
{
	public interface IResultSetColumn
	{
		void AddValueFromReader(DbDataReader reader);
		bool GetBool(int row);
		byte GetByte(int row);
		char GetChar(int row);
		DateTime GetDateTime(int row);
		decimal GetDecimal(int row);
		double GetDouble(int row);
		float GetFloat(int row);
		Int16 GetInt16(int row);
		Int32 GetInt32(int row);
		Int64 GetInt64(int row);
		UInt16 GetUInt16(int row);
		UInt32 GetUInt32(int row);
		UInt64 GetUInt64(int row);
		string GetString(int row);
		sbyte GetSByte(int row);
		object GetObject(int row);

		int Count { get; }

		string Name { get; }

		int Ordinal { get; }
	}

	public class ResultSetColumn<T> : Collection<T>, IResultSetColumn
	{
		public ResultSetColumn(string name, int ordinal)
		{
			this.Name = name;
			this.Ordinal = ordinal;
		}

		public string Name { get; protected set; }
		public int Ordinal { get; protected set; }

		public virtual void AddValueFromReader(DbDataReader reader) { throw new NotImplementedException(); }
		public virtual bool GetBool(int row) { return Convert.ToBoolean(this[row]); }
		public virtual byte GetByte(int row) { return Convert.ToByte(this[row]); }
		public virtual char GetChar(int row) { return Convert.ToChar(this[row]); }
		public virtual DateTime GetDateTime(int row) { return Convert.ToDateTime(this[row]); }
		public virtual Decimal GetDecimal(int row) { return Convert.ToDecimal(this[row]); }
		public virtual Double GetDouble(int row) { return Convert.ToDouble(this[row]); }
		public virtual float GetFloat(int row) { throw new NotImplementedException(); }
		public virtual Int16 GetInt16(int row) { return Convert.ToInt16(this[row]); }
		public virtual Int32 GetInt32(int row) { return Convert.ToInt32(this[row]); }
		public virtual Int64 GetInt64(int row) { return Convert.ToInt64(this[row]); }
		public virtual UInt16 GetUInt16(int row) { return Convert.ToUInt16(this[row]); }
		public virtual UInt32 GetUInt32(int row) { return Convert.ToUInt32(this[row]); }
		public virtual UInt64 GetUInt64(int row) { return Convert.ToUInt64(this[row]); }
		public virtual string GetString(int row) { return this[row].ToString(); }
		public virtual sbyte GetSByte(int row) { return Convert.ToSByte(this[row]); }

		public object GetObject(int row) { return this[row]; }
	}

	public class ResultSetColumnBool : ResultSetColumn<bool>
	{
		public ResultSetColumnBool(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? false : reader.GetBoolean(Ordinal));
		}

		public override bool GetBool(int row) { return this[row]; }
	}

	public class ResultSetColumnByte : ResultSetColumn<byte>
	{
		public ResultSetColumnByte(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? byte.MinValue : reader.GetByte(Ordinal));
		}

		public override byte GetByte(int row) { return this[row]; }
	}

	public class ResultSetColumnChar : ResultSetColumn<char>
	{
		public ResultSetColumnChar(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? char.MinValue : reader.GetChar(Ordinal));
		}

		public override char GetChar(int row) { return this[row]; }
	}

	public class ResultSetColumnDateTime : ResultSetColumn<DateTime>
	{
		public ResultSetColumnDateTime(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? DateTime.MinValue : reader.GetDateTime(Ordinal));
		}

		public override DateTime GetDateTime(int row) { return this[row]; }
	}

	public class ResultSetColumnDecimal : ResultSetColumn<decimal>
	{
		public ResultSetColumnDecimal(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? decimal.MinValue : reader.GetDecimal(Ordinal));
		}

		public override decimal GetDecimal(int row) { return this[row]; }
	}

	public class ResultSetColumnDouble : ResultSetColumn<double>
	{
		public ResultSetColumnDouble(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? double.MinValue : reader.GetDouble(Ordinal));
		}

		public override double GetDouble(int row) { return this[row]; }
	}

	public class ResultSetColumnFloat : ResultSetColumn<float>
	{
		public ResultSetColumnFloat(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? float.MinValue : reader.GetFloat(Ordinal));
		}

		public override float GetFloat(int row) { return this[row]; }
	}

	public class ResultSetColumnInt16 : ResultSetColumn<Int16>
	{
		public ResultSetColumnInt16(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? Int16.MinValue : reader.GetInt16(Ordinal));
		}

		public override Int16 GetInt16(int row) { return this[row]; }
	}

	public class ResultSetColumnUInt16 : ResultSetColumn<UInt16>
	{
		public ResultSetColumnUInt16(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? UInt16.MinValue : reader.GetFieldValue<UInt16>(Ordinal));
		}

		public override UInt16 GetUInt16(int row) { return this[row]; }
	}

	public class ResultSetColumnInt32 : ResultSetColumn<Int32>
	{
		public ResultSetColumnInt32(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? Int16.MinValue : reader.GetInt16(Ordinal));
		}

		public override Int32 GetInt32(int row) { return this[row]; }
	}

	public class ResultSetColumnUInt32 : ResultSetColumn<UInt32>
	{
		public ResultSetColumnUInt32(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? UInt32.MinValue : reader.GetFieldValue<UInt32>(Ordinal));
		}

		public override UInt32 GetUInt32(int row) { return this[row]; }
	}

	public class ResultSetColumnInt64 : ResultSetColumn<Int64>
	{
		public ResultSetColumnInt64(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? Int64.MinValue : reader.GetInt64(Ordinal));
		}

		public override Int64 GetInt64(int row) { return this[row]; }
	}

	public class ResultSetColumnUInt64 : ResultSetColumn<UInt64>
	{
		public ResultSetColumnUInt64(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? UInt64.MinValue : reader.GetFieldValue<UInt64>(Ordinal));
		}

		public override UInt64 GetUInt64(int row) { return this[row]; }
	}

	public class ResultSetColumnString : ResultSetColumn<string>
	{
		public ResultSetColumnString(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? null : reader.GetString(Ordinal));
		}

		public override string GetString(int row) { return this[row]; }
	}

	public class ResultSetColumnSByte : ResultSetColumn<sbyte>
	{
		public ResultSetColumnSByte(string name, int ordinal)
			: base(name, ordinal)
		{ }

		public override void AddValueFromReader(DbDataReader reader)
		{
			Add(reader.IsDBNull(Ordinal) ? sbyte.MinValue : (sbyte)reader.GetByte(Ordinal));
		}

		public override sbyte GetSByte(int row) { return this[row]; }
	}
}
